package org.mentalsmash.crossroads;

import java.util.List;

import org.apache.log4j.Logger;
import org.mentalsmash.crossroads.Crossword.CrosswordEntry;


import alice.tuprolog.InvalidTheoryException;
import alice.tuprolog.NoSolutionException;
import alice.tuprolog.Prolog;
import alice.tuprolog.SolveInfo;
import alice.tuprolog.Term;
import alice.tuprolog.Theory;
import alice.tuprolog.UnknownVarException;

/**
 * 
 * @author Andrea Reale
 *
 */
public class PrologCrosswordSolver implements SolvingEngine {

	private static Logger log = Logger.getLogger(PrologCrosswordSolver.class);
	private Prolog _prologEngine;
	private PrologExporter _exporter;
	
	/**
	 * Build a solver for a crossword which uses a prolog engine to generate solutions.
	 * The solver uses words from a dictionary source to solve the schema
	 * @param dict a class implementing DictionarySource interface for building dictionaries for crossword entries
	 */
	public PrologCrosswordSolver(PrologExporter exp)
	{
		setPrologEngine(new Prolog());
		setExporter(exp);
	}
	
	/**
	 * Set the prolog engine used by this solver
	 * @param prologEngine a tuProlog instance
	 */
	private void setPrologEngine(Prolog prologEngine) {
		_prologEngine = prologEngine;
	}



	/**
	 * @return the prolog engine used by this solver
	 */
	private Prolog getPrologEngine() {
		return _prologEngine;
	}
	




	// implementation of the crossword solver using prolog!
	@Override 
	public void solve(Crossword toSolve) throws SolvingException{
		

		String theory = getExporter().toExternalRepresentation(toSolve);
		SolveInfo solution = null;
		
		log.debug("Finding solutions...");
		try {
			getPrologEngine().setTheory(new Theory(theory));
		} catch (InvalidTheoryException e) {
			log.fatal("Auto-generated gol is malformed");
			System.exit(-2);
		}
		
		Term goal = Term.createTerm(getExporter().getMainRuleHead(toSolve));

		solution = getPrologEngine().solve(goal);
		
		
		if(!solution.isSuccess()){
			log.debug("No solutions found.");
			throw new SolvingException("No solutions found.");
		}
		log.debug("A solution was found.");
		if(solution.hasOpenAlternatives())
			log.warn("Not every possible solution was explored");
		
		log.debug("Setting solutions in the crossword matrix...");
		setSolutions(toSolve,solution);
		log.debug("Done.");
		
		

	}

	/**
	 * Sets the solution in the Crossword object
	 * @param toSolve the crossword instance to set the solutions in
	 * @param solution a VALID solution instance for the crossword toSolve
	 */
	private void setSolutions(Crossword toSolve, SolveInfo solution) {
		List<CrosswordEntry> across = toSolve.getOrderedAcrossEntries();
		List<CrosswordEntry> down = toSolve.getOrderedDownEntries();
		try{
			for(CrosswordEntry entry : across){
				String sol = solution.getTerm("E_"+entry.getId().toLowerCase()).toString();
				log.debug("Solution for entry " + entry.getId() + " is " + sol);
				toSolve.setSolution(entry.getId(), sol);
			}
			for(CrosswordEntry entry : down){
				String sol = solution.getTerm("E_"+entry.getId().toLowerCase()).toString();
				log.debug("Solution for entry " + entry.getId() + " is " + sol);
				toSolve.setSolution(entry.getId(), sol);
			}
		}catch (UnknownVarException e){
			log.fatal("Auto-generate var was unknown. " + e.getMessage());
			e.printStackTrace();
			System.exit(-23);
		} catch (NoSolutionException e){
			log.fatal("There should always be a solution at this point. " + e.getMessage());
			e.printStackTrace();
			System.exit(-23);
		} catch(CrosswordException e) {
			log.fatal("Length constraint were not succesfully resolved.", e);
			e.printStackTrace();
			System.exit(-23);
		} 
		
		
	}

	public void setExporter(PrologExporter exporter) {
		_exporter = exporter;
	}

	public PrologExporter getExporter() {
		return _exporter;
	}

}
